package com.cq.hd.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cq.hd.common.enums.biz.MonthSettleBillStatusEnum;
import com.cq.hd.common.enums.biz.WithdrawalStateEnum;
import com.cq.hd.common.response.Throw;
import com.cq.hd.member.api.TbBusinessApi;
import com.cq.hd.member.api.vo.BusinessVo;
import com.cq.hd.order.api.dto.MonthSettleBillDto;
import com.cq.hd.order.api.dto.MonthSettleBillWithdrawalDto;
import com.cq.hd.order.api.vo.MonthSettleBillVo;
import com.cq.hd.order.mapper.TbMonthSettleBillMapper;
import com.cq.hd.order.mapper.TbMonthSettleBillWithdrawalMapper;
import com.cq.hd.order.po.TbMonthSettleBillPo;
import com.cq.hd.order.po.TbMonthSettleBillWithdrawalPo;
import com.cq.hd.order.processor.settle.MerchantAmtDailySettleProcessor;
import com.cq.hd.order.processor.settle.MerchantSubsidyMonthSettleProcessor;
import com.cq.hd.order.service.TbMonthSettleBillService;
import com.cq.hd.order.service.TbMonthSettleBillWithdrawalService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 月度结算单
 *
 * @author much
 * @since 2024/04/09 23:37
 */
@Slf4j
@Service
public class TbMonthSettleBillServiceImpl implements TbMonthSettleBillService {

    @Resource
    private MerchantSubsidyMonthSettleProcessor merchantSubsidyMonthSettleProcessor;
    @Resource
    private MerchantAmtDailySettleProcessor merchantAmtDailySettleProcessor;
    @Resource
    private TbMonthSettleBillMapper monthSettleBillMapper;
    @Autowired
    private PlatformTransactionManager transactionManager;
    @Resource
    private TbMonthSettleBillWithdrawalMapper monthSettleBillWithdrawalMapper;
    @Autowired
    private TbBusinessApi businessApi;
    @Autowired
    private TbMonthSettleBillWithdrawalService orderSettleBillWithdrawalService;

    /**
     * 执行月度结算，生成月度结算账单
     * 执行对象：orderSettleBill商户补贴
     */
    @Override
    public void executeMonthlySettlement() {
        merchantSubsidyMonthSettleProcessor.run();
    }

    /**
     * 执行商户本金二次结算 for 结算池商户本金提现提现
     */
    @Override
    public void executeMerchantAmtSettlement() {
        merchantAmtDailySettleProcessor.run();
    }

    @Override
    public List<MonthSettleBillVo> getMerchantWaitMonthSettleBill(Long merchantId, Integer type) {
        MonthSettleBillDto monthSettleBillDto = new MonthSettleBillDto();
        monthSettleBillDto.setStatus(MonthSettleBillStatusEnum.NEW.getValue());
        monthSettleBillDto.setMerchantId(merchantId);
        monthSettleBillDto.setType(type);
        return getMonthSettleBillVoList(monthSettleBillDto);
    }

    /**
     * 根据商户id，月度结算单id，获取待提现的结算单列表
     */
    @Override
    public List<MonthSettleBillVo> getAvailableMonthSettleBillByIds(Long merchantId, List<Long> ids) {
        MonthSettleBillDto monthSettleBillDto = new MonthSettleBillDto();
        monthSettleBillDto.setStatus(MonthSettleBillStatusEnum.NEW.getValue());
        monthSettleBillDto.setMerchantId(merchantId);
        monthSettleBillDto.setIds(ids);
        return getMonthSettleBillVoList(monthSettleBillDto);
    }

    private List<MonthSettleBillVo> getMonthSettleBillVoList(MonthSettleBillDto monthSettleBillDto) {
        List<MonthSettleBillVo> monthSettleBillVos = monthSettleBillMapper.listByDto(monthSettleBillDto);
        List<Long> businessIdList = monthSettleBillVos.stream()
                .map(MonthSettleBillVo::getBusinessId)
                .distinct()
                .collect(Collectors.toList());
        List<BusinessVo> businessVos = businessApi.listByIds(businessIdList).unpack();
        Map<Long, BusinessVo> businessVoMap = businessVos.stream()
                .collect(Collectors.toMap(BusinessVo::getId, Function.identity()));

        monthSettleBillVos.forEach(vo -> {
            BusinessVo business = businessVoMap.get(vo.getBusinessId());
            if (business != null) {
                vo.setBusinessShortName(business.getBusinessShortName());
                vo.setBusinessFinancialAccountType(business.getFinancialAccountType());
            }
        });

        return monthSettleBillVos;
    }

    /**
     * 更新月度结算单提现状态并写入 提现-月度结算单 映射关系表
     */
    @Override
    public int withdrawalByDto(List<MonthSettleBillWithdrawalDto> monthSettleBillWithdrawalDtos) {

        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());

        int count = 0;
        try {
            for (MonthSettleBillWithdrawalDto withdrawalDto : monthSettleBillWithdrawalDtos) {
                count += withdrawalByIds(withdrawalDto);
            }
            if (count != monthSettleBillWithdrawalDtos.size()) {
                Throw.isBusinessException("月度结算单-提现记录映射关系更新失败!");
            }

            transactionManager.commit(transaction);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(transaction);
            log.error("<更新月度结算单-提现关系表失败>事务异常回滚", e);
            return 0;
        }

        return count;
    }

    @Override
    public boolean withdrawalUpdateStatus(Long withdrawalId, Integer auditState) {
        List<MonthSettleBillVo> monthSettleBillVos = orderSettleBillWithdrawalService.listMonthSettleBillByWithdrawalId(withdrawalId);
        if (monthSettleBillVos == null || monthSettleBillVos.isEmpty()) {
            return true;
        }
        for (MonthSettleBillVo monthSettleBillVo : monthSettleBillVos) {
            // 如果不是 提现中，说明状态变了，直接返回失败
            if (monthSettleBillVo.getStatus() != MonthSettleBillStatusEnum.IN_PROGRESS.getValue()) {
                return false;
            }
        }

        List<Long> monthSettleBillIds = monthSettleBillVos.stream()
                .map(MonthSettleBillVo::getId)
                .collect(Collectors.toList());

        log.info("【提现审核】withdrawalId:{}, auditState: {}, monthSettleBillIds: {}", withdrawalId, auditState, monthSettleBillIds);

        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        try {
            TbMonthSettleBillPo monthSettleBillPo = new TbMonthSettleBillPo();
            if (WithdrawalStateEnum.AUDIT_SUCCESS.getValue().equals(auditState)) {
                monthSettleBillPo.setStatus(MonthSettleBillStatusEnum.FINISHED.getValue());
            } else {
                monthSettleBillPo.setStatus(MonthSettleBillStatusEnum.NEW.getValue());

                // 如果是审核不通过，就需要删掉 月度结算单-提现记录关联表
                monthSettleBillWithdrawalMapper.delete(new QueryWrapper<TbMonthSettleBillWithdrawalPo>()
                        .eq("withdrawal_id", withdrawalId));
            }
            monthSettleBillPo.setUpdateTime(LocalDateTime.now());
            int count = monthSettleBillMapper.update(monthSettleBillPo, new QueryWrapper<TbMonthSettleBillPo>()
                    .eq("status", MonthSettleBillStatusEnum.IN_PROGRESS.getValue())
                    .in("id", monthSettleBillIds));
            if (count != monthSettleBillIds.size()) {
                Throw.isBusinessException("[提现审核]结算单状态更新失败，数量不一致");
            }

            transactionManager.commit(transaction);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(transaction);
            log.error("【提现审核】事务异常回滚", e);
            return false;
        }

        return true;
    }

    private int withdrawalByIds(MonthSettleBillWithdrawalDto withdrawalDto) {
        Long merchantId = withdrawalDto.getMerchantId();
        Long withdrawalId = withdrawalDto.getWithdrawalId();
        List<Long> monthSettleBillIds = withdrawalDto.getMonthSettleBillIds();

        // 确保结算单还没被申请
        List<MonthSettleBillVo> monthSettleBillVos = getAvailableMonthSettleBillByIds(merchantId, monthSettleBillIds);
        if (monthSettleBillVos.size() != monthSettleBillIds.size()) {
            Throw.isBusinessException("结算单状态有误，请刷新后重新申请");
        }

        // 1. 把结算单状态更新成 提现中 todo (这里需要看原有的逻辑，会不会有定时脚本，针对提现中的，做什么不良的处理)
        TbMonthSettleBillPo monthSettleBillPo = new TbMonthSettleBillPo();
        monthSettleBillPo.setStatus(MonthSettleBillStatusEnum.IN_PROGRESS.getValue());
        monthSettleBillPo.setUpdateTime(LocalDateTime.now());
        int count = monthSettleBillMapper.update(monthSettleBillPo, new QueryWrapper<TbMonthSettleBillPo>()
                .in("id", monthSettleBillIds));
        if (count != monthSettleBillIds.size()) {
            Throw.isBusinessException("结算单状态更新失败1");
        }

        // 2. 删除旧的 月度结算单-提现记录 映射表
        monthSettleBillWithdrawalMapper.delete(new QueryWrapper<TbMonthSettleBillWithdrawalPo>()
                .eq("withdrawal_id", withdrawalId));

        // 3. 写入新的 月度结算单-提现记录 映射表
        return batchSaveMonthSettleBillWithdrawal(withdrawalId, monthSettleBillIds) ? 1 : 0;
    }

    /**
     * 批量插入月度结算账单-提现记录映射关系
     */
    private boolean batchSaveMonthSettleBillWithdrawal(Long withdrawalId, List<Long> monthSettleBillIds) {
        List<TbMonthSettleBillWithdrawalPo> tbMonthSettleBillWithdrawalPos = new ArrayList<>();
        for (Long monthSettleBillId : monthSettleBillIds) {
            TbMonthSettleBillWithdrawalPo tbMonthSettleBillWithdrawalPo = new TbMonthSettleBillWithdrawalPo();
            tbMonthSettleBillWithdrawalPo.setWithdrawalId(withdrawalId);
            tbMonthSettleBillWithdrawalPo.setMonthSettleBillId(monthSettleBillId);
            tbMonthSettleBillWithdrawalPos.add(tbMonthSettleBillWithdrawalPo);
            tbMonthSettleBillWithdrawalPo.setCreateTime(LocalDateTime.now());
            tbMonthSettleBillWithdrawalPo.setUpdateTime(LocalDateTime.now());
        }
        return orderSettleBillWithdrawalService.saveBatch(tbMonthSettleBillWithdrawalPos);
    }
}
